#include "GraphicalEntity.h"
#include "GlobalInstances.h"

#define DRAGCOEFF 0.5f

IDirect3DVertexDeclaration9 *Vertex::Decl = NULL;

void DeclareVertexDecl( void )
{
	D3DVERTEXELEMENT9 VertexPNTElements[] = 
	{
		{0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		D3DDECL_END()
	};	
	HR(gd3dDevice->CreateVertexDeclaration(VertexPNTElements, &Vertex::Decl));
}
void DeleteVertexDecl( void )
{
	HR(Vertex::Decl->Release());
}

//-----------------------//
//CLASS: BASE MESH ENTITY//
//-----------------------//

BaseMeshEntity::BaseMeshEntity()
{
	Mesh = NULL;
	Device = NULL;
}

BaseMeshEntity::BaseMeshEntity(IDirect3DDevice9 *nDevice)
{
	Device = nDevice;
	Mesh = NULL;
}

BaseMeshEntity::~BaseMeshEntity()
{
	/*if(Mesh)
	{
		Mesh->Release();
	}*/
	if(Material)
		delete Material;
	if(Texture)
		delete Texture;
}

void BaseMeshEntity::Update( float dt )
{
	Position += Velocity * dt;
}

void BaseMeshEntity::Render( void )
{
	for( DWORD i = 0; i < numMaterials; i++ )
        {
            // Set the material and texture for this subset
			if(&Material[i] != NULL)
				Device->SetMaterial( &Material[i] );
			else 
				return;

			if(Texture)
				Device->SetTexture( 0, Texture[i] );
			else
				return;
            // Draw the mesh subset
            Mesh->DrawSubset( i );
        }
}

HRESULT BaseMeshEntity::LoadXFile( std::string Filename, DWORD Options )
{
	//Creates a pointer to a buffer for materials and sets it to NULL
	LPD3DXBUFFER pD3DXMtrlBuffer = NULL;

	//If it succeeds in loading the model, continue through the rest of the code.
	GameModel *TempModel;

	HRESULT tempHR = S_OK;

 	if(DoesModelExist(Filename))
	{
		UINT id = GetModelNumber(Filename);
		Mesh = Globals->GameModels[id]->MainModel;
		pD3DXMtrlBuffer = Globals->GameModels[id]->MaterialBuffer;
		numMaterials = Globals->GameModels[id]->NumberMaterials;
	}
	else
	{
		TempModel = new GameModel;
		tempHR = D3DXLoadMeshFromXA(Filename.c_str(), Options, Device, NULL, &TempModel->MaterialBuffer, NULL, &TempModel->NumberMaterials, &TempModel->MainModel);
		
		if(tempHR != S_OK)
		{
			MessageBox(NULL, "There has been a problem loading a model", Filename.c_str(), MB_OK);
			delete TempModel;
			TempModel = NULL;
			return S_FALSE;
		}

		TempModel->FileName = Filename;
		Mesh = TempModel->MainModel;
		pD3DXMtrlBuffer = TempModel->MaterialBuffer;
		numMaterials = TempModel->NumberMaterials;

		Globals->GameModels.push_back(TempModel);
	}



	//HRESULT tempHR = D3DXLoadMeshFromXA(Filename.c_str(), Options, Device, NULL, &pD3DXMtrlBuffer, NULL, &numMaterials, &Mesh);
	if( tempHR == S_OK)
	{
		//Creates a material pointer and places the previous buffer's pointer in there.
		D3DXMATERIAL* d3dxMaterials = ( D3DXMATERIAL* )pD3DXMtrlBuffer->GetBufferPointer();

		//Creates a material array for the number of materials you need, numMaterials is set by the 
		//D3DXLoadMeshFromX() function.
		Material = new D3DMATERIAL9[numMaterials];

		//If you can't allocate more memory, it returns a flag that states you are out of memory
		if( Material == NULL )
			return E_OUTOFMEMORY;
		
		//Creates an array of texture pointers to store the textures in, numMaterials was set by the
		//D3DXLoadMeshFromX() function
		Texture = new LPDIRECT3DTEXTURE9[numMaterials];

		//If you are unable to allocate any memory for the texture array, it returns a flag for telling you
		//that you are out of memory
		if( Texture == NULL )
			return E_OUTOFMEMORY;

		//Starting of the for loop, to take care of the materials & textures
		for( DWORD i = 0; i < numMaterials; i++ )
		{
			// Copy the material
			Material[i] = d3dxMaterials[i].MatD3D;

			// Set the ambient color for the material (D3DX does not do this)
			Material[i].Ambient = Material[i].Diffuse;

			//Sets the texture pointer to NULL, so that it is a set value and 
			//not a garbage value
			Texture[i] = NULL;

			//If the texture has a filename and it's filename's length is greater than 0...
			if( d3dxMaterials[i].pTextureFilename != NULL &&
				lstrlenA( d3dxMaterials[i].pTextureFilename ) > 0 )
			{
				// Create the texture
				GameTexture *TempTexture;

				const CHAR* strPrefix = "Models/";
				CHAR strTexture[MAX_PATH];
				ClearString(strTexture, MAX_PATH);
				strcpy_s( strTexture, MAX_PATH, strPrefix );
				strcat_s( strTexture, MAX_PATH, d3dxMaterials[i].pTextureFilename );

				//Check for texture
				if(DoesTextureExist(strTexture))
				{
					Texture[i] = Globals->GameTextures[GetTextureNumber(strTexture)]->MainTexture;
				}
				else
				{
					TempTexture = new GameTexture;
					// If texture is not in current folder, try parent folder
					if( FAILED( D3DXCreateTextureFromFileA( Device,
						strTexture,
						&TempTexture->MainTexture ) ) )
					{	//Only called if you cannot find the texture in the parent or current folder
						MessageBox( NULL, "Could not find texture map", Filename.c_str(), MB_OK );
						return S_FALSE;
					}

					TempTexture->FileName = strTexture;

					Globals->GameTextures.push_back(TempTexture);

					Texture[i] = TempTexture->MainTexture;

				}
			}
		}
		//Releases memory used by the material buffer
		//pD3DXMtrlBuffer->Release();
		//Sets material buffer to NULL
		//pD3DXMtrlBuffer = NULL;
	}
	else if(tempHR == -2005531760)
	{
		MessageBox(NULL, "There has been a parsing error in the model file!", Filename.c_str(), MB_OK);
		return S_FALSE;
	}
	else
	{
		//if the file fails to load, message box appears to tell you.
		MessageBox(NULL, "Model file cannot be found!", Filename.c_str(), MB_OK);
		return S_FALSE;
	}
	return S_OK;
}

void BaseMeshEntity::onLostDevice( void )
{
	//Texture->
}

void BaseMeshEntity::onResetDevice( void )
{

}

//----------------------------//
//CLASS: BASE GRAPHICAL ENTITY//
//----------------------------//

BaseGraphicalEntity::BaseGraphicalEntity()
{
	VertexBuffer = NULL;
	Device = NULL;
}

BaseGraphicalEntity::BaseGraphicalEntity(IDirect3DDevice9 *nDevice)
{
	Device = nDevice;
	VertexBuffer = NULL;
}

BaseGraphicalEntity::~BaseGraphicalEntity()
{
	if(VertexBuffer)
	{
		VertexBuffer->Release();
	}
}

void BaseGraphicalEntity::Update(float dt)
{
	Position += Velocity * dt;
	
	if(Velocity.x > 0)
	{
		Velocity.x -= Velocity.x * 0.5f * dt;
	}
	if(Velocity.x < 0)
	{
		Velocity.x -= Velocity.x * 0.5f * dt;
	}
	if(Velocity.y > 0)
	{
		Velocity.y -= Velocity.y * 0.5f * dt;	
	}
	if(Velocity.y < 0)
	{
		Velocity.y -= Velocity.y * 0.5f * dt;
	}
	if(Velocity.z > 0)
	{
		Velocity.z -= Velocity.z * 0.5f * dt;
	}
	if(Velocity.z < 0)
	{
		Velocity.z -= Velocity.z * 0.5f * dt;
	}
	
	if(Velocity.x < 1.e-005f && Velocity.x > -1.e-005f)
		Velocity.x = 0;
	if(Velocity.y < 1.e-005f && Velocity.y > -1.e-005f)
		Velocity.y = 0;
	if(Velocity.z < 1.e-005f && Velocity.z > -1.e-005f)
		Velocity.z = 0;
}

void BaseGraphicalEntity::Render( void )
{
	//TEST CODE
	gd3dDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(Vertex));
	gd3dDevice->SetVertexDeclaration(Vertex::Decl);
	gd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, numVertices);
	//TEST CODE
}

bool BaseGraphicalEntity::CreateQuad( D3DXVECTOR3 HalfVector)
{
	gd3dDevice->CreateVertexBuffer(6 * sizeof(Vertex), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &VertexBuffer, 0);
	HRESULT hrs = 0;
	Vertex* v = NULL;
	
	D3DXCOLOR tempColor = D3DCOLOR_XRGB(255,255,255);
	//D3DXCOLOR tempColor = D3DCOLOR_XRGB(rand()+255,rand()+255,rand()+255);
	hrs = (VertexBuffer->Lock(0, 0, (void**)&v, 0));
	
	v[0] = Vertex((-HalfVector.x), 0, (-HalfVector.z), tempColor, 0.0, 0.0);
	v[1] = Vertex((-HalfVector.x), 0, ( HalfVector.z), tempColor, 0.0, 1.0);
	v[2] = Vertex(( HalfVector.x), 0, ( HalfVector.z), tempColor, 1.0, 1.0);

	v[3] = Vertex((-HalfVector.x), 0, (-HalfVector.z), tempColor, 0.0, 0.0);
	v[4] = Vertex(( HalfVector.x), 0, ( HalfVector.z), tempColor, 1.0, 1.0);
	v[5] = Vertex(( HalfVector.x), 0, (-HalfVector.z), tempColor, 1.0, 0.0);
	
	numVertices = 6;

	hrs = (VertexBuffer->Unlock());
	hrs = 0;
	return true;
}

void BaseGraphicalEntity::onLostDevice( void )
{

}


void BaseGraphicalEntity::onResetDevice( void )
{

}

//BASEINDEXEDGRAPHICALENTITY CLASS//

BaseIndexedGraphicalEntity::BaseIndexedGraphicalEntity()
	: BaseGraphicalEntity()
{

}

BaseIndexedGraphicalEntity::BaseIndexedGraphicalEntity(IDirect3DDevice9 *Device)
	:BaseGraphicalEntity(Device)
{
	IndexBuffer = NULL;
}

BaseIndexedGraphicalEntity::~BaseIndexedGraphicalEntity( void )
{
	if(IndexBuffer)
		IndexBuffer->Release();
}

void BaseIndexedGraphicalEntity::Update(float dt)
{
	BaseGraphicalEntity::Update(dt);
}

void BaseIndexedGraphicalEntity::Render( void )
{
	HRESULT hr = S_OK;
	if(VertexBuffer)
	{
		hr = gd3dDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(Vertex));
		if(hr != S_OK)
			return;

		gd3dDevice->SetVertexDeclaration(Vertex::Decl);
		gd3dDevice->SetIndices(IndexBuffer);
		gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, (numIndices/3));
	}
}

bool BaseIndexedGraphicalEntity::CreateCube(D3DXVECTOR3 HalfVector)
{
	if(VertexBuffer)
		VertexBuffer->Release();
	if(IndexBuffer)
		IndexBuffer->Release();

	gd3dDevice->CreateVertexBuffer(8 * sizeof(Vertex), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &VertexBuffer, 0);
	gd3dDevice->CreateIndexBuffer(36 * sizeof(int*), D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_MANAGED, &IndexBuffer, 0);

	HRESULT hrs;
	Vertex* v = NULL;
	
	D3DXCOLOR tempColor = D3DCOLOR_XRGB(rand(),rand(),rand());

	hrs = (VertexBuffer->Lock(0, 0, (void**)&v, 0));
	
	//1,1,1
	v[0] = Vertex(( HalfVector.x), HalfVector.y, ( HalfVector.z), tempColor, 0.0, 0.0);
	//1,1,-1
	v[1] = Vertex((HalfVector.x), HalfVector.y, (-HalfVector.z), tempColor, 0.0, 1.0);
	//-1,1,1
	v[2] = Vertex((-HalfVector.x), HalfVector.y, ( HalfVector.z), tempColor, 1.0, 0.0);
	//-1,1,-1
	v[3] = Vertex((-HalfVector.x), HalfVector.y, (-HalfVector.z), tempColor, 1.0, 1.0);

	//1,-1,1
	v[4] = Vertex(( HalfVector.x), -HalfVector.y, ( HalfVector.z), tempColor, 1.0, 1.0);
	//-1,-1,1
	v[5] = Vertex((-HalfVector.x), -HalfVector.y, ( HalfVector.z), tempColor, 0.0, 1.0);
	//1,-1,-1
	v[6] = Vertex(( HalfVector.x), -HalfVector.y, (-HalfVector.z), tempColor, 1.0, 0.0);
	//-1,-1,-1
	v[7] = Vertex((-HalfVector.x), -HalfVector.y, (-HalfVector.z), tempColor, 0.0, 0.0);

	numVertices = 8;

	hrs = (VertexBuffer->Unlock());
	hrs = 0;

	unsigned int Indices[] = 
	{
			//1
			3, 1, 7,
			7, 1, 6,
			//2
			2, 3, 5,
			5, 3, 7,
			//3
			4, 0, 5,
			5, 0, 2,
			//4
			6, 1, 4,
			4, 1, 0,
			//5
			2, 0, 3,
			3, 0, 1,
			//6
			6, 4, 7,
			7, 4, 5

	};

	void *pVoid;

	hrs = (IndexBuffer->Lock(0, 0, (void**)&pVoid, 0));

	memcpy(pVoid, Indices, sizeof(Indices));

	hrs = (IndexBuffer->Unlock());

	numIndices = 36;
	return S_OK;

}

bool BaseIndexedGraphicalEntity::CreateCube(D3DXVECTOR3 HalfVector, D3DXCOLOR nColor)
{
	if(VertexBuffer)
		VertexBuffer->Release();
	if(IndexBuffer)
		IndexBuffer->Release();

	gd3dDevice->CreateVertexBuffer(8 * sizeof(Vertex), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &VertexBuffer, 0);
	gd3dDevice->CreateIndexBuffer(36 * sizeof(int*), D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_MANAGED, &IndexBuffer, 0);

	HRESULT hrs;
	Vertex* v = NULL;
	
	hrs = (VertexBuffer->Lock(0, 0, (void**)&v, 0));
	
	//1,1,1
	v[0] = Vertex(( HalfVector.x), HalfVector.y, ( HalfVector.z), nColor, 0.0, 0.0);
	//1,1,-1
	v[1] = Vertex((HalfVector.x), HalfVector.y, (-HalfVector.z), nColor, 0.0, 1.0);
	//-1,1,1
	v[2] = Vertex((-HalfVector.x), HalfVector.y, ( HalfVector.z), nColor, 1.0, 0.0);
	//-1,1,-1
	v[3] = Vertex((-HalfVector.x), HalfVector.y, (-HalfVector.z), nColor, 1.0, 1.0);

	//1,-1,1
	v[4] = Vertex(( HalfVector.x), -HalfVector.y, ( HalfVector.z), nColor, 1.0, 1.0);
	//-1,-1,1
	v[5] = Vertex((-HalfVector.x), -HalfVector.y, ( HalfVector.z), nColor, 0.0, 1.0);
	//1,-1,-1
	v[6] = Vertex(( HalfVector.x), -HalfVector.y, (-HalfVector.z), nColor, 1.0, 0.0);
	//-1,-1,-1
	v[7] = Vertex((-HalfVector.x), -HalfVector.y, (-HalfVector.z), nColor, 0.0, 0.0);

	numVertices = 8;

	hrs = (VertexBuffer->Unlock());
	hrs = 0;

	unsigned int Indices[] = 
	{
			//1
			3, 1, 7,
			7, 1, 6,
			//2
			2, 3, 5,
			5, 3, 7,
			//3
			4, 0, 5,
			5, 0, 2,
			//4
			6, 1, 4,
			4, 1, 0,
			//5
			2, 0, 3,
			3, 0, 1,
			//6
			6, 4, 7,
			7, 4, 5

	};

	void *pVoid;

	hrs = (IndexBuffer->Lock(0, 0, (void**)&pVoid, 0));

	memcpy(pVoid, Indices, sizeof(Indices));

	hrs = (IndexBuffer->Unlock());

	numIndices = 36;
	return S_OK;
}

void BaseIndexedGraphicalEntity::onLostDevice( void )
{
	
}


void BaseIndexedGraphicalEntity::onResetDevice( void )
{

}

bool DoesTextureExist(std::string FileName)
{
	UINT size = Globals->GameTextures.size();
	for(UINT i = 0; i < size; ++i)
	{
		if(Globals->GameTextures[i]->FileName == FileName)
			return true;
	}

	return false;
}

UINT GetTextureNumber(std::string FileName)
{
	UINT size = Globals->GameTextures.size();
	for(UINT i = 0; i < size; ++i)
	{
		if(Globals->GameTextures[i]->FileName == FileName)
			return i;
	}
	
	return 0;
}

void ClearString( char* ClearMe, UINT size)
{
	for(UINT i = 0; i < size; ++i)
		ClearMe[i] = ' ';
}

bool DoesModelExist(std::string FileName)
{
	UINT size = Globals->GameModels.size();
	for(UINT i = 0; i < size; ++i)
	{
		if(Globals->GameModels[i]->FileName == FileName)
			return true;
	}

	return false;
}

UINT GetModelNumber(std::string FileName)
{
	UINT size = Globals->GameModels.size();
	for(UINT i = 0; i < size; ++i)
	{
		if(Globals->GameModels[i]->FileName == FileName)
			return i;
	}

	return 0;
}